/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <string>

#include "yaffut.h"
#include "myassert.h"
#include "TestSession.h"

#include "Area.h"
#include "Room.h"
#include "MudExit.h"
#include "MudObject.h"
#include "Error.h"
#include "MudPlayer.h"
#include "MudCreature.h"
#include "color.h"

using namespace std;
using namespace Chronos;

class TestObject : public MudObject {
public:
	TestObject(const string& name, const string& descr, bool* destroyed_)
		: MudObject(name, descr)
		, destroyed(destroyed_)
	{}
	virtual ~TestObject() {
		*destroyed = true;
	}
private:
	bool* destroyed;
};


class RoomListenerObject : public MudObject {
public:
	RoomListenerObject(const string& name)
		: MudObject(name, "")
        , lastMobile(0)
    {}
    virtual bool handleRoomEvent(MudMobile& mobile, RoomEvent) {
        lastMobile = &mobile;    
        return true;
    }
    void clear() { lastMobile = 0; }
    MudMobile* lastMobile;
};


class RoomTest : public Room { 
public:
	RoomTest() : Room("roomName", "roomDescription")
	{}
};


TEST(RoomTest, testRoomDescriptions) {
    Area area(0, "");
	ASSERT_STR_EQUAL("roomName\n", getShortDescription());
	ASSERT_STR_EQUAL("roomName\nroomDescription\n", getLongDescription());

	addObject(new MudObject("rock", "a nice rock.\n"));
	ASSERT_STR_EQUAL("roomName\n", getShortDescription());

	MudObject* northExit = new MudNorthExit(area, 2);
	addObject(northExit);
	ASSERT_STR_EQUAL("roomName ("ANSI_YELLOW"north"ANSI_NORMAL")\n", getShortDescription());

	MudObject* eastExit = new MudEastExit(area, 3);
	addObject(eastExit);
	ASSERT_STR_EQUAL("roomName ("ANSI_YELLOW"north"ANSI_NORMAL", "ANSI_YELLOW"east"ANSI_NORMAL")\n", getShortDescription());

	removeObject(eastExit);
	ASSERT_STR_EQUAL("roomName ("ANSI_YELLOW"north"ANSI_NORMAL")\n", getShortDescription());

	removeObject(northExit);
	ASSERT_STR_EQUAL("roomName\n", getShortDescription());
}


TEST(RoomTest, roomDescructorShouldDeleteAllObjects) {
	bool destroyed1 = false;
	bool destroyed2 = false;
	{
		Room room("roomName", "roomDescription");
		room.addObject(new TestObject("obj1", "an object1", &destroyed1));
		room.addObject(new TestObject("obj2", "an object2", &destroyed2));
	}
	ASSERT_TRUE(destroyed1);
	ASSERT_TRUE(destroyed2);
}


TEST(RoomTest, testShouldAddRemoveObjects) {
	MudObject* obj1 = new MudObject("test1", "test object1");
	addObject(obj1);
	ASSERT_TRUE(obj1->getRoom() == this);

	removeObject(obj1);
	ASSERT_NULL(obj1->getRoom());

	try {
		removeObject(obj1);
		ASSERT_FAIL();
	} catch (MudError& e) {
		ASSERT_STR_EQUAL("Room: object test1 not in room", e.what());
	}
}


TEST(RoomTest, testShouldAddRemoveMultipleObjectsWithSameName) {
	MudObject* obj1 = new MudObject("test", "test object1");
	MudObject* obj2 = new MudObject("test", "test object2");
	addObject(obj1);
	addObject(obj2);

	MudObject* found = findObject("test");
	ASSERT_TRUE(found == obj1);
}


TEST(RoomTest, testShouldFindObjects) {
	MudObject* orig = new MudObject("rock", "a nice rock.\n");
	addObject(orig);
	MudObject* found = findObject("rock");
	ASSERT_TRUE(found == orig);

	MudObject* found2 = findObject("nonexist");
	ASSERT_NULL(found2);
}


TEST(RoomTest, roomShouldNotifyListenersAboutMobileArrival) {
    RoomListenerObject listener("obj1");
    addObject(&listener);
    ASSERT_NULL(listener.lastMobile);

    addObject(new MudObject("obj2", ""));
    ASSERT_NULL(listener.lastMobile);

    MudMobile* mob = new MudCreature("creature", "", 1, 1, 1, 1);
    addObject(mob);
    ASSERT_TRUE(listener.lastMobile == mob);
    listener.clear();

    removeObject(&listener);
    MudMobile* mob2 = new MudCreature("creature", "", 1, 1, 1, 1);
    addObject(mob2);
    ASSERT_NULL(listener.lastMobile);

}


TEST(RoomTest, testShouldAddRemovePlayer) {
	TestSession session("player1");	
	MudPlayer& player1 = session.getPlayer();
	addPlayer(player1);

	ASSERT_TRUE(player1.getRoom() == this);
	session.SESSION_CHECKWRITTEN("roomName\n");

	MudObject* found = findObject("player1");
	ASSERT_TRUE(found == &player1);

	removePlayer(player1);
	ASSERT_NULL(player1.getRoom());
	ASSERT_NULL(findObject("player1"));
	session.SESSION_CHECKWRITTEN("");

	try {
		removePlayer(player1, "");
		ASSERT_FAIL();
	} catch (MudError& e) {
		ASSERT_STR_EQUAL("Room: cannot find player player1 in room roomName", e.what());
	}
}


TEST(RoomTest, testShouldAddRemoveMultiplePlayers) {
	TestSession session1("player1");	
	addPlayer(session1.getPlayer(), "player1 lands.");
	session1.SESSION_CHECKWRITTEN("roomName\n");

	TestSession session2("player2");	
	addPlayer(session2.getPlayer(), "player2 appears.");
	session2.SESSION_CHECKWRITTEN("roomName\n");
	session1.SESSION_CHECKWRITTEN("player2 appears.\n");

	removePlayer(session2.getPlayer(), "player2 leaves.");
	session1.SESSION_CHECKWRITTEN("player2 leaves.\n");
	removePlayer(session1.getPlayer());
}


TEST(RoomTest, testShouldShowText) {
	TestSession session1("player1");	
	addPlayer(session1.getPlayer());

	TestSession session2("player2");	
	addPlayer(session2.getPlayer());

	TestSession session3("player3");	
	addPlayer(session3.getPlayer());
	session1.clear();
	session2.clear();
	session3.clear();

	showText("test message1");
	session1.SESSION_CHECKWRITTEN("test message1\n");
	session2.SESSION_CHECKWRITTEN("test message1\n");
	session3.SESSION_CHECKWRITTEN("test message1\n");

	showText("test message2", session1.getPlayer());
	session1.SESSION_CHECKWRITTEN("");
	session2.SESSION_CHECKWRITTEN("test message2\n");
	session3.SESSION_CHECKWRITTEN("test message2\n");

	showText("test message3", session1.getPlayer(), session2.getPlayer());
	session1.SESSION_CHECKWRITTEN("");
	session2.SESSION_CHECKWRITTEN("");
	session3.SESSION_CHECKWRITTEN("test message3\n");

	removePlayer(session1.getPlayer());
	removePlayer(session2.getPlayer());
	removePlayer(session3.getPlayer());
}

